Package org.python.pydev.debug.ui

Source Code of org.python.pydev.debug.ui.DebuggerTestWorkbench

/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.debug.ui;

import java.io.ByteArrayInputStream;
import java.util.HashSet;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.WorkbenchException;
import org.python.pydev.debug.model.PyDebugTarget;
import org.python.pydev.debug.model.PyVariable;
import org.python.pydev.debug.ui.actions.PyBreakpointRulerAction;
import org.python.pydev.debug.ui.launching.JythonLaunchShortcut;
import org.python.pydev.editor.PyEdit;
import org.python.pydev.editor.codecompletion.revisited.javaintegration.AbstractWorkbenchTestCase;
import org.python.pydev.editorinput.PyOpenEditor;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.utils.ICallback;

public class DebuggerTestWorkbench extends AbstractWorkbenchTestCase {

    /**
     * File used to debug
     */
    private IFile debugFile;

    /**
     * The editor that'll be created on debug
     */
    private PyEdit debugEditor;

    /**
     * Maximum number of loops (used with the timeout)
     */
    private final int MAX_LOOPS = 300;

    /**
     * Maximum time for each loop in millis
     */
    private final int STEP_TIMEOUT = 100;

    /**
     * Number of steps in the tests that will have busy loops until some condition is hit.
     */
    private final int TOTAL_STEPS = 3;

    /**
     * Total time in millis that the test has for finishing
     */
    private final int TOTAL_TIME_FOR_TESTS = MAX_LOOPS * STEP_TIMEOUT * (TOTAL_STEPS + 1);

    /**
     * Used for having wait()
     */
    private Object lock = new Object();

    /**
     * An exception that occurred that was thrown and didn't let the tests finish
     */
    private Throwable failException = null;

    /**
     * Only true when the test finishes without exceptions.
     */
    private boolean finished = false;

    private String currentStep = "<unspecified>";

    /**
     * Creates the debug file and editor.
     */
    @Override
    protected void setUp() throws Exception {
        super.setUp();
        debugFile = initFile.getParent().getFile(new Path("debug_file.py"));
        String mod1Contents = "from pack1.pack2 import mod1\nprint mod1\nprint 'now'\n";
        debugFile.create(new ByteArrayInputStream(mod1Contents.getBytes()), true, null);
        debugFile.refreshLocal(IResource.DEPTH_ZERO, null);

        debugEditor = (PyEdit) PyOpenEditor.doOpenEditor(debugFile);
    }

    /**
     * Removes the debug file and closes the debug editor
     */
    @Override
    protected void tearDown() throws Exception {
        super.tearDown();
        if (debugFile != null) {
            debugFile.delete(true, null);
        }
        if (debugEditor != null) {
            debugEditor.close(false);
        }
    }

    /**
     * In this test, a thread is started and then we wait on a busy loop until the thread finishes with the tests.
     */
    public void testDebugger() throws Exception {

        //start the thread that'll do the test
        threadTest.start();

        //wait on a busy loop until the test is finished or an exception is thrown.
        goToManual(TOTAL_TIME_FOR_TESTS, new com.aptana.shared_core.callbacks.ICallback<Boolean, Object>() {

            public Boolean call(Object arg) {
                return finished || failException != null;
            }
        });

        //Make it fail if we encountered some problem
        if (failException != null) {
            failException.printStackTrace();
            fail("Current Step: " + currentStep + "\n" + failException.getMessage());
        }
        if (!finished) {
            if (failException == null) {
                fail("Current Step: " + currentStep + "\nThe test didn't finish in the available time: "
                        + TOTAL_TIME_FOR_TESTS / 1000 + " secs.");
            }
        }
    }

    /**
     * This is the thread that'll make the test.
     */
    Thread threadTest = new Thread() {
        @Override
        public void run() {
            try {
                currentStep = "launchEditorInDebug";
                //make a launch for debugging
                launchEditorInDebug();

                //switch to debug perspective, because otherwise, when we hit a breakpoint it'll ask if we want to show it.
                switchToPerspective("org.eclipse.debug.ui.DebugPerspective");
                PyBreakpointRulerAction createAddBreakPointAction = createAddBreakPointAction(1);
                createAddBreakPointAction.run();

                currentStep = "waitForLaunchAvailable";
                ILaunch launch = waitForLaunchAvailable();
                PyDebugTarget target = (PyDebugTarget) waitForDebugTargetAvailable(launch);

                currentStep = "waitForSuspendedThread";
                IThread suspendedThread = waitForSuspendedThread(target);
                assertTrue(suspendedThread.getName().startsWith("MainThread"));
                IStackFrame topStackFrame = suspendedThread.getTopStackFrame();
                assertTrue("Was not expecting: " + topStackFrame.getName(),
                        topStackFrame.getName().indexOf("debug_file.py:2") != 0);
                IVariable[] variables = topStackFrame.getVariables();

                HashSet<String> varNames = new HashSet<String>();
                for (IVariable variable : variables) {
                    PyVariable var = (PyVariable) variable;
                    varNames.add(var.getName());
                }
                HashSet<String> expected = new HashSet<String>();
                expected.add("Globals");
                expected.add("__doc__");
                expected.add("__file__");
                expected.add("__name__");
                expected.add("mod1");
                assertEquals(expected, varNames);

                assertTrue(target.canTerminate());
                target.terminate();

                finished = true;
            } catch (Throwable e) {
                failException = e;
            }
        }

    };

    /**
     * Creates a run in debug mode for the debug editor
     */
    private void launchEditorInDebug() {
        final IWorkbench workBench = PydevPlugin.getDefault().getWorkbench();
        Display display = workBench.getDisplay();

        // Make sure to run the UI thread.
        display.syncExec(new Runnable() {
            public void run() {
                JythonLaunchShortcut launchShortcut = new JythonLaunchShortcut();
                launchShortcut.launch(debugEditor, "debug");
            }
        });

    }

    /**
     * @return an action that can be run to create a breakpoint in the given line
     */
    private PyBreakpointRulerAction createAddBreakPointAction(final int line) {
        PyBreakpointRulerAction ret = new PyBreakpointRulerAction(debugEditor, new IVerticalRulerInfo() {
            public int getLineOfLastMouseButtonActivity() {
                return line;
            }

            public Control getControl() {
                throw new RuntimeException("Not Implemented");
            }

            public int getWidth() {
                throw new RuntimeException("Not Implemented");
            }

            public int toDocumentLineNumber(int y_coordinate) {
                throw new RuntimeException("Not Implemented");
            }
        });
        ret.update();
        return ret;
    }

    /**
     * This method can be used to switch to a given perspective
     * @param perspectiveId the id of the perspective that should be activated.
     */
    protected void switchToPerspective(final String perspectiveId) {
        final IWorkbench workBench = PydevPlugin.getDefault().getWorkbench();
        Display display = workBench.getDisplay();

        // Make sure to run the UI thread.
        display.syncExec(new Runnable() {
            public void run() {
                IWorkbenchWindow window = workBench.getActiveWorkbenchWindow();
                try {
                    workBench.showPerspective(perspectiveId, window);
                } catch (WorkbenchException e) {
                    failException = e;
                }
            }
        });
    }

    /**
     * Waits until some thread is suspended.
     */
    protected IThread waitForSuspendedThread(final PyDebugTarget target) throws Throwable {
        final IThread[] ret = new IThread[1];

        waitForCondition(new ICallback() {

            public Object call(Object args) throws Exception {
                IThread[] threads = target.getThreads();
                for (IThread thread : threads) {
                    if (thread.isSuspended()) {
                        ret[0] = thread;
                        return true;
                    }
                }
                return false;
            }
        }, "waitForSuspendedThread");

        return ret[0];
    }

    /**
     * Waits until a launch becomes available
     * @return the launch that was found
     */
    private ILaunch waitForLaunchAvailable() throws Throwable {
        final ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
        waitForCondition(new ICallback() {

            public Object call(Object args) throws Exception {
                ILaunch[] launches = launchManager.getLaunches();
                return launches.length > 0;
            }
        }, "waitForLaunchAvailable");
        return launchManager.getLaunches()[0];
    }

    /**
     * Waits until a debug target is available in the passed launch
     * @return the debug target found
     */
    private IDebugTarget waitForDebugTargetAvailable(final ILaunch launch) throws Throwable {
        waitForCondition(new ICallback() {

            public Object call(Object args) throws Exception {
                return launch.getDebugTarget() != null;
            }
        }, "waitForDebugTargetAvailable");

        return launch.getDebugTarget();
    }

    /**
     * Keeps on a busy loop with a timeout until the given callback returns true (otherwise, an
     * exception is thrown when the total time is elapsed).
     */
    private void waitForCondition(ICallback callback, String errorMessage) throws Throwable {
        if (failException != null) {
            throw failException;
        }

        int loops = MAX_LOOPS;
        for (int i = 0; i < loops; i++) {
            if ((Boolean) callback.call(new Object[] {})) {
                return;
            }
            synchronized (lock) {
                try {
                    Thread.yield();
                    lock.wait(STEP_TIMEOUT);
                } catch (InterruptedException e) {
                }
            }
        }
        fail("Unable to get to condition after " + (loops * STEP_TIMEOUT) / 1000 + " seconds.\nMessage: "
                + errorMessage);
    }

}
TOP

Related Classes of org.python.pydev.debug.ui.DebuggerTestWorkbench

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.